/*
 *  1942Machine.cpp
 *  1942reem
 *
 *  Created by James on 8/4/08.
 *  Copyright 2008 __MyCompanyName__. All rights reserved.
 *
 */

#include "StdAfx.h"
#include "1942Machine.h"

void C1942Machine::DecodePalette()
{
#if 0
   34  PALETTE_INIT( 1942 )
   35  {
   36      rgb_t palette[256];
   37      int i, colorbase;
   38  
   39      for (i = 0; i < 256; i++)
   40      {
   41          int bit0,bit1,bit2,bit3,r,g,b;
   42  
   43          /* red component */
   44          bit0 = (color_prom[i + 0*256] >> 0) & 0x01;
   45          bit1 = (color_prom[i + 0*256] >> 1) & 0x01;
   46          bit2 = (color_prom[i + 0*256] >> 2) & 0x01;
   47          bit3 = (color_prom[i + 0*256] >> 3) & 0x01;
   48          r = 0x0e * bit0 + 0x1f * bit1 + 0x43 * bit2 + 0x8f * bit3;
   49          /* green component */
   50          bit0 = (color_prom[i + 1*256] >> 0) & 0x01;
   51          bit1 = (color_prom[i + 1*256] >> 1) & 0x01;
   52          bit2 = (color_prom[i + 1*256] >> 2) & 0x01;
   53          bit3 = (color_prom[i + 1*256] >> 3) & 0x01;
   54          g = 0x0e * bit0 + 0x1f * bit1 + 0x43 * bit2 + 0x8f * bit3;
   55          /* blue component */
   56          bit0 = (color_prom[i + 2*256] >> 0) & 0x01;
   57          bit1 = (color_prom[i + 2*256] >> 1) & 0x01;
   58          bit2 = (color_prom[i + 2*256] >> 2) & 0x01;
   59          bit3 = (color_prom[i + 2*256] >> 3) & 0x01;
   60          b = 0x0e * bit0 + 0x1f * bit1 + 0x43 * bit2 + 0x8f * bit3;
   61  
   62          palette[i] = MAKE_RGB(r,g,b);
   63      }
   64  
   65      color_prom += 3*256;
   66      /* color_prom now points to the beginning of the lookup table */
   67  
   68  
   69      /* characters use palette entries 128-143 */
   70      colorbase = 0;
   71      for (i = 0; i < 64*4; i++)
   72          palette_set_color(machine, colorbase + i, palette[0x80 | *color_prom++]);
   73      colorbase += 64*4;
   74  
   75      /* background tiles use palette entries 0-63 in four banks */
   76      for (i = 0; i < 32*8; i++)
   77      {
   78          palette_set_color(machine, colorbase + 0*32*8 + i, palette[0x00 | *color_prom]);
   79          palette_set_color(machine, colorbase + 1*32*8 + i, palette[0x10 | *color_prom]);
   80          palette_set_color(machine, colorbase + 2*32*8 + i, palette[0x20 | *color_prom]);
   81          palette_set_color(machine, colorbase + 3*32*8 + i, palette[0x30 | *color_prom]);
   82          color_prom++;
   83      }
   84      colorbase += 4*32*8;
   85  
   86      /* sprites use palette entries 64-79 */
   87      for (i = 0; i < 16*16; i++)
   88          palette_set_color(machine, colorbase + i, palette[0x40 | *color_prom++]);
   89  }
#endif

	int i;
	unsigned char b0,b1,b2,b3;
	unsigned char r,g,b;

	//create the palette
	for(i=0;i<256;i++) {
	
		b0 = ((PROM[i] >> 0) & 1) * 0x0E;
		b1 = ((PROM[i] >> 1) & 1) * 0x1F;
		b2 = ((PROM[i] >> 2) & 1) * 0x43;
		b3 = ((PROM[i] >> 3) & 1) * 0x8F;
		r = b0 + b1 + b2 + b3;
	
		b0 = ((PROM[i + 256] >> 0) & 1) * 0x0E;
		b1 = ((PROM[i + 256] >> 1) & 1) * 0x1F;
		b2 = ((PROM[i + 256] >> 2) & 1) * 0x43;
		b3 = ((PROM[i + 256] >> 3) & 1) * 0x8F;
		g = b0 + b1 + b2 + b3;

		b0 = ((PROM[i + 512] >> 0) & 1) * 0x0E;
		b1 = ((PROM[i + 512] >> 1) & 1) * 0x1F;
		b2 = ((PROM[i + 512] >> 2) & 1) * 0x43;
		b3 = ((PROM[i + 512] >> 3) & 1) * 0x8F;
		b = b0 + b1 + b2 + b3;
		
		Palette[i] = (r << 16) | (g << 8) | b;
	}

	for(i=0;i<256;i++) {
		BackgroundLookup[i] = 0x00 | PROM[i + 1024];
		BackgroundLookup[i + 256] = 0x10 | PROM[i + 1024];
		BackgroundLookup[i + 512] = 0x20 | PROM[i + 1024];
		BackgroundLookup[i + 768] = 0x30 | PROM[i + 1024];
		SpriteLookup[i] = 0x40 | PROM[i + 2048];
		CharLookup[i] = 0x80 | PROM[i + 768];
	}
}


void C1942Machine::DrawChar(unsigned char *dest,int pitch,unsigned char tile)
{
	int x,y;
	unsigned char pixel,*tiledata = &CharROM[tile * 16];

	for(y=0;y<8;y++) {
		for(x=0;x<4;x++) {
			pixel = (tiledata[0] >> (x + 0)) & 1;
			pixel |= ((tiledata[0] >> (x + 4)) & 1) << 1;
			dest[x+0] = CharLookup[pixel];
			pixel = (tiledata[8] >> (x + 0)) & 1;
			pixel |= ((tiledata[8] >> (x + 4)) & 1) << 1;
			dest[x+4] = CharLookup[pixel];
		}
		tiledata += 1;
		dest += pitch;
	}
}

void C1942Machine::DrawTile(unsigned char *dest,int pitch,unsigned char tile)
{
	int x,y;
	unsigned char pixel,*tiledata = &TileROM[tile * 16];

	for(y=0;y<8;y++) {
		for(x=0;x<16;x++) {
			pixel = (tiledata[0] >> (x + 0)) & 1;
			pixel |= ((tiledata[0] >> (x + 4)) & 1) << 1;
			dest[x+0] = pixel;
			pixel = (tiledata[8] >> (x + 0)) & 1;
			pixel |= ((tiledata[8] >> (x + 4)) & 1) << 1;
			dest[x+4] = pixel;
		}
		tiledata += 1;
		dest += pitch;
	}
}

C1942Machine::C1942Machine()
{
	CPU[0] = new CDeadZ80();
	CPU[1] = new CDeadZ80();

	MainCPU = (CDeadZ80*)CPU[0];
	SoundCPU = (CDeadZ80*)CPU[1];

	memset(Screen,0,256 * 256);

	AddLoadInfo(ROM_CPU1,MainROM);
	AddLoadInfo(ROM_CPU2,SoundROM);
	AddLoadInfo(ROM_VROM1,CharROM);
	AddLoadInfo(ROM_VROM2,TileROM);
	AddLoadInfo(ROM_VROM3,SpriteROM);
	AddLoadInfo(ROM_PROM,PROM);
}

C1942Machine::~C1942Machine()
{
	Kill();
}

int C1942Machine::Init()
{
	//initialize the z80's
	MainCPU->Init();
	SoundCPU->Init();

	//set user pointer
	MainCPU->SetUser(this);
	SoundCPU->SetUser(this);

	//set read/write functions
	MainCPU->SetRead(ReadMainCPU);
	MainCPU->SetWrite(WriteMainCPU);
	SoundCPU->SetRead(ReadSoundCPU);
	SoundCPU->SetWrite(WriteSoundCPU);
	
	MainCPU->SetIRQ(MainIRQ);
	SoundCPU->SetIRQ(SoundIRQ);

	//initialize the ay8910's
	AY8910_InitClock(1500000);
	AY8910_InitAll(1500000,44100);	//2x 1.5mhz, 44100hz output

	return(0);
}

void C1942Machine::Kill()
{
	Sound->SetCallback(0);
}

void C1942Machine::Reset()
{
	int i,n;

	for(n=0;n<0x10000;n++)
		MainROM[n] = SoundROM[n] = 0;

	LoadROMs();

	DecodePalette();

	ProcessInput();

	DoSoundIRQ = 0;

	Scanline = 0;
	VBlank = 0x80;

	for(i=0;i<0x800;i++)
		{
/*		MainRAM[i] = 0;
		VideoRAM[i & 0x3FF] = 0;
		ColorRAM[i & 0x3FF] = 0;
		PaletteRAM[i & 0xF] = 0;
		Palette[i & 0xF] = 0;
*/		}

	BankPtr = MainROM + 0x8000;
	BankNum = 0;

	Sound->SetCallback(ProcessSound);

	AY8910_reset(0);
	AY8910_reset(1);

	MainCPU->Reset();
	SoundCPU->Reset();

	//HighScoresLoaded = 0;
	//if(RomSet->GetHighScoreInfo() == 0)
	//	HighScoresLoaded++;
		
	message("machine reset\n");
}

void C1942Machine::Blit()
{
	Video->StartBlit();
	RenderScreen(Screen,256);
	BlitRotate(Screen,(u32*)Video->GetSurface(),Palette);
	Video->StopBlit();
}

void C1942Machine::Line(u32 mainc,u32 soundc,int irq)
{
	if(Scanline < 256 && (Scanline & 0xF) == 0 && irq)
		{
//		if(SoundIRQControl)
//			SoundCPU->NMI();
		}
	if(Scanline == 0) {
		VBlank = 0;
		DoSoundIRQ = 1;
		MainCPU->IRQ();
	}
	else if(Scanline == 10 || Scanline == 132 || Scanline == 196 || Scanline == 256)
		SoundCPU->IRQ();
	else if(Scanline == 240)
		{
		Blit();
		ProcessInput();
		VBlank = 0x80;
		MainCPU->IRQ();
		}
	if(MainCPU->Execute(mainc) & BAD_OPCODE) //main cpu (1500000 / 57) cycles per frame
		{
		message("main cpu: bad opcode at $%04X ($%02X)\n",MainCPU->GetPC(),0);//MainCPU->GetOpcode());
		return;
		}
	if(SoundCPU->Execute(soundc) & BAD_OPCODE) //sound cpu (500000 / 57) cycles per frame
		{
		message("sound cpu: bad opcode at $%04X ($%02X)\n",SoundCPU->GetPC(),0);//SoundCPU->GetOpcode());
		return;
		}
	Scanline++;
	if(Scanline >= 272)
		Scanline = 0;
	//if(HighScoresLoaded == 0)
	//	CheckHighScores();
}

void C1942Machine::Frame()
{
	do
		{
//		Line(97,33,1);
		Line(4000000 / 272 / 60,3000000 / 272 / 60,1);						//speed up sound cpu
		} while(Scanline != 0);
}

void C1942Machine::SaveState(CState *s)
{
//	int n;

//	MainCPU->SaveState(s);
//	SoundCPU->SaveState(s);

	s->Write((u8*)AY8910_GetRegsPtr(0),16);
	s->Write((u8*)AY8910_GetRegsPtr(1),16);
}

void C1942Machine::LoadState(CState *s)
{
	int n;
	u8 ay8910_regs[2][16];

//MainCPU->LoadState(s);
//SoundCPU->LoadState(s);

//all other data

s->Read(ay8910_regs[0],16);
s->Read(ay8910_regs[1],16);
for(n=0;n<16;n++)
	{
	_AYWriteReg(0,n,ay8910_regs[0][n]);
	_AYWriteReg(1,n,ay8910_regs[1][n]);
	}
}

static char highscoreident[] = "HIGHSCORE\0";
static int highscoreidentlen = 10;

void C1942Machine::SaveScores()
{
highscore_t *high = RomSet->GetHighScoreInfo();
int n;
CHighScore h;

if(high == 0)
	return;
if(h.Save(RomSet->GetName()) != 0)
	return;
h.Write((u8*)highscoreident,highscoreidentlen);
while(high->Length != -1)
	{
//	for(n=0;n<high->Length;n++)
//		h.Write8(MainCPU->Read(high->Addr + n));
	high++;
	}
h.Close();
}

void C1942Machine::LoadScores()
{
highscore_t *high = RomSet->GetHighScoreInfo();
char ident[16];
int n;
CHighScore h;

if(high == 0)
	return;
if(h.Load(RomSet->GetName()) != 0)
	return;
h.Read((u8*)ident,highscoreidentlen);
if(memcmp(ident,highscoreident,highscoreidentlen) != 0)
	{
	message("bad high score identifier (%s)\n",ident);
	return;
	}
while(high->Length != -1)
	{
//	for(n=0;n<high->Length;n++)
//		MainCPU->Write(high->Addr + n,h.Read8());
	high++;
	}
h.Close();
}

void C1942Machine::RenderScreen(u8 *dest,int pitch)
{
	RenderChars(dest,pitch);
}

void C1942Machine::RenderChars(u8 *dest,int pitch)
{
	int x,y;
	unsigned char *src = VRAM;

	for(y=0;y<32;y++) {
		for(x=0;x<32;x++)
			DrawChar(&dest[(x * 8) + (y * 8 * 256)],256,src[x]);
		src += 32;
	}
}

/*
    8  MAIN CPU:
    9  
   10  0000-bfff ROM (8000-bfff banked)
   11  cc00-cc7f Sprites
   12  d000-d3ff Video RAM
   13  d400-d7ff Color RAM
   14  d800-dbff Background RAM (groups of 32 bytes, 16 code, 16 color/attribute)
   15  e000-efff RAM
   16  
   17  read:
   18  c000      IN0
   19  c001      IN1
   20  c002      IN2
   21  c003      DSW0
   22  c004      DSW1
   23  
   24  write:
   25  c800      command for the audio CPU
   26  c802-c803 background scroll
   27  c804      bit 7: flip screen
   28            bit 4: cpu B reset
   29            bit 0: coin counter
   30  c805      background palette bank selector
   31  c806      bit 0-1 ROM bank selector 00=1-N5.BIN
   32                                      01=1-N6.BIN
   33                                      10=1-N7.BIN
*/
u8 C1942Machine::ReadMainCPU(void *user,u32 addr)
{
	C1942Machine *M = (C1942Machine*)user;

	if(addr < 0x8000)
		return(M->MainROM[addr]);

	else if(addr < 0xC000)
		return(M->BankPtr[addr - 0x8000]);

	else if(addr >= 0xCC00 && addr < 0xD000)
		return(M->SpriteRAM[addr & 0x7F]);

	else if(addr >= 0xD000 && addr < 0xE000)
		return(M->VRAM[addr & 0xFFF]);

	else if(addr >= 0xE000 && addr < 0xF000)
		return(M->RAM[addr & 0xFFF]);

	else if(addr == 0xC000)
		return(M->InputData[0]);

	else if(addr == 0xC001)
		return(M->InputData[1]);

	else if(addr == 0xC002)
		return(M->InputData[2]);

	else if(addr == 0xC003)
		return(M->Dip[0]);

	else if(addr == 0xC004)
		return(M->Dip[1]);

	message("unhandled read (main cpu, addr $%04X)\n",addr);
	return(0);
}

void C1942Machine::WriteMainCPU(void *user,u32 addr,u8 data)
{
	C1942Machine *M = (C1942Machine*)user;

	if(addr >= 0xD000 && addr < 0xE000)
		M->VRAM[addr & 0xFFF] = data;

	else if(addr >= 0xE000 && addr < 0xF000)
		M->RAM[addr & 0xFFF] = data;

	else if(addr >= 0xCC00 && addr < 0xD000)
		M->SpriteRAM[addr & 0x7F] = data;

	else if(addr == 0xC800) {		//sound cpu command
		M->SoundLatch = data;
//		M->DoSoundIRQ = 1;
//		M->MainCPU->IRQ();
//		M->SoundCPU->IRQ();
	}

	else if(addr == 0xC804) {		//misc
		if(data & 0x10)				//reset sound cpu
			M->SoundCPU->Reset();
	}

	else if(addr == 0xC806) {		//bankswitch
		M->BankNum = data;
		M->BankPtr = M->MainROM + 0x8000 + ((data & 3) * 0x4000);
	}
	
	else
		message("unhandled write (main cpu, $%04X = $%02X)\n",addr,data);
}

u8 C1942Machine::ReadSoundCPU(void *user,u32 addr)
{
	C1942Machine *M = (C1942Machine*)user;

	if(addr < 0x4000)
		return(M->SoundROM[addr]);

	else if(addr < 0x4800)
		return(M->SoundRAM[addr & 0x7FF]);

	else if(addr == 0x6000)
		return(M->SoundLatch);

	message("unhandled read (sound cpu, addr $%04X)\n",addr);
	return(0);
}

void C1942Machine::WriteSoundCPU(void *user,u32 addr,u8 data)
{
	C1942Machine *M = (C1942Machine*)user;

	if(addr >= 0x4000 && addr < 0x4800)
		M->SoundRAM[addr & 0x7FF] = data;
		
	else if(addr == 0x8000)
		AY8910Write(0,1,data);
		
	else if(addr == 0x8001)
		AY8910Write(0,0,data);
		
	else if(addr == 0xC000)
		AY8910Write(1,1,data);
		
	else if(addr == 0xC001)
		AY8910Write(1,0,data);
		
	else
		message("unhandled write (main cpu, $%04X = $%02X)\n",addr,data);
}

u8 C1942Machine::MainIRQ(void *user)
{
	C1942Machine *M = (C1942Machine*)user;

	if(M->DoSoundIRQ) {
		M->DoSoundIRQ = 0;
		return(0xCF);	//rst 08h
	}
	return(0xD7);	//rst 10h
}

u8 C1942Machine::SoundIRQ(void *user)
{
	C1942Machine *M = (C1942Machine*)user;

	return(0xFF);	//rst 38h
}

void C1942Machine::ProcessSound(void *user,void *data,int len)
{
	s16 *dest = (s16*)data;
	s16 b1[3][1024];
	s16 b2[3][1024];
	int n;

	AY8910Update(0,(INT16*)b1[0],(INT16*)b1[1],(INT16*)b1[2],len);
	AY8910Update(1,(INT16*)b2[0],(INT16*)b2[1],(INT16*)b2[2],len);

	for(n=0;n<len;n++)
		dest[n] = (b1[0][n] + b1[1][n] + b1[2][n] + b2[0][n] + b2[1][n] + b2[2][n]) / 6;
}
